package crmdna.api.endpoint;

import static crmdna.common.AssertUtils.ensure;

import java.util.Date;
import java.util.List;

import javax.annotation.Nullable;
import javax.inject.Named;
import javax.servlet.http.HttpServletRequest;

import com.google.api.server.spi.config.Api;
import com.google.api.server.spi.config.ApiMethod;
import com.google.api.server.spi.config.ApiMethod.HttpMethod;
import com.google.appengine.api.users.User;
import com.google.gson.Gson;

import crmdna.client.Client;
import crmdna.common.APIResponse;
import crmdna.common.APIResponse.Status;
import crmdna.common.DateUtils;
import crmdna.common.DateUtils.DateRange;
import crmdna.common.RequestInfo;
import crmdna.common.UnitUtils.PhysicalQuantity;
import crmdna.common.UnitUtils.ReportingUnit;
import crmdna.common.Utils;
import crmdna.group.Group;
import crmdna.hr.Department;
import crmdna.inventory.InventoryItem;
import crmdna.inventory.InventoryItemChange;
import crmdna.inventory.InventoryItemChangeProp;
import crmdna.inventory.InventoryItemChangeQueryCondition;
import crmdna.inventory.InventoryItemProp;
import crmdna.inventory.InventoryItemQueryCondition;
import crmdna.inventory.InventoryItemType;

@Api(name = "inventoryItem")
public class InventoryItemApi {

	@ApiMethod(path = "addNewItem", httpMethod = HttpMethod.POST)
	public APIResponse addNewItem(
			@Nullable @Named("client") String client,
			@Nullable @Named("groupIdOrName") String groupIdOrName,
			@Named("inventoryItemTypeIdOrName") String inventoryItemTypeIdOrName,
			@Named("displayName") String displayName,
			@Named("physicalQuantity") PhysicalQuantity physicalQuantity,
			@Named("reportingUnit") ReportingUnit reportingUnit,
			@Nullable @Named("initialQuantityInReportingUnitDefaultZero") Double initialQuantityInReportingUnit,
			@Nullable @Named("showStackTrace") Boolean showStackTrace,
			HttpServletRequest req, User user) {

		if (client == null)
			client = "isha";

		if (null == initialQuantityInReportingUnit)
			initialQuantityInReportingUnit = 0.0;

		String login = null;

		try {
			Client.ensureValid(client);

			login = Utils.getLoginEmail(user);

			if (groupIdOrName == null)
				groupIdOrName = "mahamudra";

			long groupId = 0;
			if (Utils.canParseAsLong(groupIdOrName)) {
				groupId = Utils.safeParseAsLong(groupIdOrName);
			} else {
				groupId = Group.safeGetByName(client, groupIdOrName).toProp().groupId;
			}
			ensure(groupId > 0);

			long inventoryItemTypeId = 0;
			if (Utils.canParseAsLong(inventoryItemTypeIdOrName)) {
				inventoryItemTypeId = Utils
						.safeParseAsLong(inventoryItemTypeIdOrName);
			} else {
				inventoryItemTypeId = InventoryItemType.safeGetByName(client,
						inventoryItemTypeIdOrName).toProp().inventoryItemTypeId;
			}
			ensure(inventoryItemTypeId > 0);

			InventoryItemProp prop = InventoryItem.create(client, groupId,
					inventoryItemTypeId, displayName, physicalQuantity,
					reportingUnit, initialQuantityInReportingUnit, login);

			return new APIResponse(Status.SUCCESS, prop);

		} catch (Exception ex) {
			return Utils.toAPIResponse(ex, showStackTrace, new RequestInfo()
					.client(client).req(req).login(login));
		}
	}

	@ApiMethod(path = "updateAttributes", httpMethod = HttpMethod.POST)
	public APIResponse updateAttributes(
			@Nullable @Named("client") String client,
			@Named("inventoryItemId") long inventoryItemId,
			@Nullable @Named("newInventoryItemTypeId") Long newInventoryItemTypeId,
			@Nullable @Named("newDisplayName") String newDisplayName,
			@Nullable @Named("newReportingUnit") ReportingUnit newReportingUnit,
			@Nullable @Named("showStackTrace") Boolean showStackTrace,
			HttpServletRequest req, User user) {

		if (client == null)
			client = "isha";

		String login = null;

		try {
			Client.ensureValid(client);

			login = Utils.getLoginEmail(user);

			InventoryItemProp prop = InventoryItem.update(client,
					inventoryItemId, newInventoryItemTypeId, newDisplayName,
					newReportingUnit, login);

			return new APIResponse(Status.SUCCESS, prop);

		} catch (Exception ex) {
			return Utils.toAPIResponse(ex, showStackTrace, new RequestInfo()
					.client(client).req(req).login(login));
		}
	}

	@ApiMethod(path = "checkin", httpMethod = HttpMethod.POST)
	public APIResponse checkin(@Nullable @Named("client") String client,
			@Named("inventoryItemIdOrName") String inventoryItemIdOrName,
			@Named("quantity") double changeInReportingUnit,
			@Named("unit") ReportingUnit reportingUnit,
			@Nullable @Named("changeDescription") String changeDescription,
			@Nullable @Named("showStackTrace") Boolean showStackTrace,
			HttpServletRequest req, User user) {

		String login = null;

		if (client == null)
			client = "isha";

		try {

			long inventoryItemId = 0;
			if (Utils.canParseAsLong(inventoryItemIdOrName)) {
				inventoryItemId = Utils.safeParseAsLong(inventoryItemIdOrName);
			} else {
				inventoryItemId = InventoryItem.safeGetByName(client,
						inventoryItemIdOrName).toProp().inventoryItemId;
			}

			ensure(changeInReportingUnit > 0,
					"Quantity should be greater than zero");

			login = Utils.getLoginEmail(user);
			InventoryItemProp prop = InventoryItem.updateQuantity(client,
					inventoryItemId, changeInReportingUnit, reportingUnit,
					changeDescription, null, false, login);

			return new APIResponse().status(Status.SUCCESS).object(prop);

		} catch (Exception ex) {
			return Utils.toAPIResponse(ex, showStackTrace, new RequestInfo()
					.client(client).req(req).login(login));
		}
	}

	@ApiMethod(path = "checkout", httpMethod = HttpMethod.POST)
	public APIResponse checkout(
			@Nullable @Named("client") String client,
			@Named("inventoryItemIdOrName") String inventoryItemIdOrName,
			@Named("quantity") double changeInReportingUnit,
			@Named("unit") ReportingUnit reportingUnit,
			@Nullable @Named("departmentIdOrName") String departmentIdOrName,
			@Nullable @Named("changeDescription") String changeDescription,
			@Nullable @Named("allowNegativeDefaultFalse") Boolean allowNegative,
			@Nullable @Named("showStackTrace") Boolean showStackTrace,
			HttpServletRequest req, User user) {

		String login = null;

		if (client == null)
			client = "isha";

		if (allowNegative == null)
			allowNegative = false;

		try {

			long inventoryItemId = 0;

			if (Utils.canParseAsLong(inventoryItemIdOrName)) {
				inventoryItemId = Utils.safeParseAsLong(inventoryItemIdOrName);
			} else {
				inventoryItemId = InventoryItem.safeGetByName(client,
						inventoryItemIdOrName).toProp().inventoryItemId;
			}

			Long departmentId = null;
			if (departmentIdOrName != null) {
				if (Utils.canParseAsLong(departmentIdOrName)) {
					departmentId = Utils.safeParseAsLong(departmentIdOrName);
				} else {
					departmentId = Department.safeGetByName(client,
							departmentIdOrName).toProp().departmentId;
				}
			}

			ensure(changeInReportingUnit > 0,
					"Quantity should be greater than zero");

			login = Utils.getLoginEmail(user);
			InventoryItemProp prop = InventoryItem.updateQuantity(client,
					inventoryItemId, -changeInReportingUnit, reportingUnit,
					changeDescription, departmentId, allowNegative, login);

			return new APIResponse().status(Status.SUCCESS).object(prop);

		} catch (Exception ex) {
			return Utils.toAPIResponse(ex, showStackTrace, new RequestInfo()
					.client(client).req(req).login(login));
		}
	}

	@ApiMethod(path = "getCurrentStockList", httpMethod = HttpMethod.GET)
	public APIResponse getCurrentStockList(
			@Nullable @Named("client") String client,
			@Nullable @Named("groupIdOrName") String groupIdOrName,
			@Nullable @Named("showStackTrace") Boolean showStackTrace,
			HttpServletRequest req, User user) {

		if (client == null)
			client = "isha";

		if (groupIdOrName == null)
			groupIdOrName = "mahamudra";

		String login = null;

		try {

			login = Utils.getLoginEmail(user);

			long groupId = 0;
			if (Utils.canParseAsLong(groupIdOrName)) {
				groupId = Utils.safeParseAsLong(groupIdOrName);
			} else {
				groupId = Group.safeGetByName(client, groupIdOrName).toProp().groupId;
			}

			InventoryItemQueryCondition qc = new InventoryItemQueryCondition();
			qc.groupId = groupId;

			List<InventoryItemProp> props = InventoryItem.query(client, qc,
					login);

			return new APIResponse().status(Status.SUCCESS).object(props);

		} catch (Exception ex) {
			return Utils.toAPIResponse(ex, showStackTrace, new RequestInfo()
					.client(client).req(req).login(login));
		}
	}

	@ApiMethod(path = "delete", httpMethod = HttpMethod.DELETE)
	public APIResponse delete(@Nullable @Named("client") String client,
			@Named("inventoryItemId") long inventoryItemId,
			@Nullable @Named("showStackTrace") Boolean showStackTrace,
			HttpServletRequest req, User user) {

		if (client == null)
			client = "isha";

		String login = null;

		try {
			Client.ensureValid(client);

			login = Utils.getLoginEmail(user);

			InventoryItem.delete(client, inventoryItemId, login);

			return new APIResponse().status(Status.SUCCESS).message(
					"Inventory item [" + inventoryItemId + "] deleted");

		} catch (Exception ex) {
			return Utils.toAPIResponse(ex, showStackTrace, new RequestInfo()
					.client(client).req(req).login(login));
		}
	}

	@ApiMethod(path = "getStockChanges", httpMethod = HttpMethod.GET)
	public APIResponse getStockChanges(
			@Nullable @Named("client") String client,
			@Nullable @Named("groupIdOrName") String groupIdOrName,
			@Nullable @Named("dateRange") DateRange dateRange,
			@Nullable @Named("startDateYYYYMMDD") Integer startYYYYMMDD,
			@Nullable @Named("endDateYYYYMMDD") Integer endYYYYMMDD,
			@Nullable @Named("inventoryItemTypeIdOrName") String inventoryItemTypeIdOrName,
			@Nullable @Named("departmentIdOrName") String departmentIdOrName,
			@Nullable @Named("inventoryItemIdOrName") String inventoryItemIdOrName,
			@Nullable @Named("sendEmail") Boolean sendEmail,
			@Nullable @Named("showStackTrace") Boolean showStackTrace,
			HttpServletRequest req, User user) {

		if (client == null)
			client = "isha";

		String login = null;

		if (groupIdOrName == null)
			groupIdOrName = "mahamudra";

		try {
			Client.ensureValid(client);
			login = Utils.getLoginEmail(user);

			ensure(((startYYYYMMDD == null) && (endYYYYMMDD == null))
					|| ((startYYYYMMDD != null) && (endYYYYMMDD != null)),
					"Both startYYYYMMDD and endYYYYMMDD should be specified (if dateRange is not specified)");

			ensure(((dateRange == null) && (startYYYYMMDD != null))
					|| ((dateRange != null) && (startYYYYMMDD == null)),
					"Either dateRange should be specified or (startYYYYMMDD and endYYYYMMDD) should be specified but not both");

			long startMS = 0;
			long endMS;
			if (dateRange != null) {
				endMS = new Date().getTime();
				startMS = endMS
						- DateUtils.getMilliSecondsFromDateRange(dateRange);
			} else {
				DateUtils.ensureFormatYYYYMMDD(startYYYYMMDD);
				DateUtils.ensureFormatYYYYMMDD(endYYYYMMDD);
				ensure(endYYYYMMDD >= startYYYYMMDD, "End date should be greater or equal to start date");

				startMS = DateUtils.toDate(startYYYYMMDD).getTime();
				endMS = DateUtils.toDate(endYYYYMMDD).getTime();
			}

			ensure(startMS != 0, "Start date cannot be 0");
			ensure(endMS != 0, "End date cannot be 0");
			ensure(endMS >= startMS, "End date should be greater or equal to start date");

			long groupId;
			if (Utils.canParseAsLong(groupIdOrName)) {
				groupId = Utils.safeParseAsLong(groupIdOrName);
			} else {
				groupId = Group.safeGetByName(client, groupIdOrName).toProp().groupId;
			}

			ensure(groupId > 0);

			Long inventoryItemTypeId = null;
			if (inventoryItemTypeIdOrName != null) {
				if (Utils.canParseAsLong(inventoryItemTypeIdOrName)) {
					inventoryItemTypeId = Utils
							.safeParseAsLong(inventoryItemTypeIdOrName);
				} else {
					inventoryItemTypeId = InventoryItemType.safeGetByName(
							client, inventoryItemTypeIdOrName).toProp().inventoryItemTypeId;
				}

				ensure(inventoryItemTypeId > 0);
			}

			Long departmentId = null;
			if (departmentIdOrName != null) {
				if (Utils.canParseAsLong(departmentIdOrName)) {
					departmentId = Utils.safeParseAsLong(departmentIdOrName);
				} else {
					departmentId = Department.safeGetByName(client,
							departmentIdOrName).toProp().departmentId;
				}

				ensure(departmentId > 0);
			}

			Long inventoryItemId = null;
			if (inventoryItemIdOrName != null) {
				if (Utils.canParseAsLong(inventoryItemIdOrName)) {
					inventoryItemId = Utils
							.safeParseAsLong(inventoryItemIdOrName);
				} else {
					inventoryItemId = InventoryItem.safeGetByName(client,
							inventoryItemIdOrName).toProp().inventoryItemId;
				}

				ensure(inventoryItemId > 0);
			}

			InventoryItemChangeQueryCondition qc = new InventoryItemChangeQueryCondition(
					groupId, startMS, endMS);
			if (departmentId != null)
				qc.departmentIds.add(departmentId);

			if (inventoryItemId != null)
				qc.inventoryItemIds.add(inventoryItemId);

			if (inventoryItemTypeId != null)
				qc.inventoryItemTypeIds.add(inventoryItemTypeId);

			List<InventoryItemChangeProp> props = InventoryItemChange.query(
					client, qc, login);

			return new APIResponse()
					.status(Status.SUCCESS)
					.message(
							"QueryCondition: [" + new Gson().toJson(qc)
									+ "]. No of changes: " + props.size())
					.object(props);

		} catch (Exception ex) {
			return Utils.toAPIResponse(ex, showStackTrace, new RequestInfo()
					.client(client).req(req).login(login));
		}
	}
}
